package com.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.base.model.R;
import com.search.mapper.MediaMapper;
import com.search.mapper.TagSearchMapper;
import com.search.model.Media;
import com.search.service.MediaSearchService;
import org.elasticsearch.action.DocWriteResponse;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * @Author: 小何
 * @Description: TODO
 * @DateTime: 2023/9/26 10:29
 **/
@Service
public class MediaSearchServiceImpl implements MediaSearchService {

    public static final Integer PAGE_SIZE = 60;

    public static final String MEDIA_INDEX = "media";

    public static final String CATEGORY_FIELD = "category";

    public static final String DURATION_FIELD = "duration";

    public static final String DELETE_FAIL = "NOT_FOUND";

    @Autowired
    private MediaMapper mediaMapper;

    @Autowired
    private TagSearchMapper tagMapper;

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    /**
     * 同步mysql数据库中的数据到es中
     *
     * @return 返回同步是否成功
     */
    @Override
    public boolean synchronousMysqlToElasticSearch() {
        //todo:使用多线程进行操作,提高同步效率
        try {
            //批量插入请求
            BulkRequest request = new BulkRequest();
            //从mysql中查询出所有的视频信息
            List<Media> medias = mediaMapper.selectList(null);
            //循环将视频数据插入批量请求中
            for (Media media : medias) {
                //向批量请求中添加请求
                request.add(new IndexRequest("media").id(media.getId().toString()).source(JSON.toJSONString(media), XContentType.JSON));
            }
            //执行批量插入操作。
            restHighLevelClient.bulk(request, RequestOptions.DEFAULT);
        } catch (IOException e) {
            return false;
        }
        return true;
    }

    /**
     * 复合查询,可通过搜索框中的内容进行搜索,支持搜索的字段为作者、标题、标签。可通过视频时长,视频分类进行过滤。
     * 可通过视频发布时间、视频播放量进行过滤。支持对结果进行分页,页面大小固定为60。
     *
     * @param context   搜索框
     * @param orderCode 排序码,根据排序码获取需要排序的字段。
     * @param page      页码,页面大小固定为60
     * @param category  分类
     * @param timeCode  时长码,根据时长码获取视频时长过滤条件的上下限。
     * @return 返回查询到的视频信息。
     */
    @Override
    public R<List<Media>> listMediaBySearchBox(String context, Integer orderCode, Integer page, Integer category, Integer timeCode) {
        try {
            //创建查询请求
            SearchRequest request = new SearchRequest(MEDIA_INDEX);
            //创建bool查询请求,是request的子级
            BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
            //判断分类是否为空,如果不为空,则根据分类进行过滤
            if (category != null) {
                boolQuery.filter(QueryBuilders.termQuery(CATEGORY_FIELD, category));
            }
            //判断时间码是否为空,不为空则通过时间码获取视频时长的上下限,通过视频时长进行过滤
            if (timeCode != null) {
                String[] timeLimitByTimeCode = getTimeLimitByTimeCode(timeCode);
                boolQuery.filter(QueryBuilders.rangeQuery(DURATION_FIELD).gt(timeLimitByTimeCode[0]).lte(timeLimitByTimeCode[1]));
            }

            //判断排序码是否为空,不为空则通过排序码进行倒序排序
            if (orderCode != null) {
                String orderFieldByOrderCode = getOrderFieldByOrderCode(orderCode);
                request.source().sort(orderFieldByOrderCode, SortOrder.DESC);
            }

            //判断页码是否为空,不为空则进行分页,页面大小固定为60
            if (page != null) {
                request.source().from((page - 1) * PAGE_SIZE).size(PAGE_SIZE);
            } else {
                request.source().from(0).size(PAGE_SIZE);
            }
            //通过all字段和boolQuery进行查询
            if (context != null && !context.equals("")) {
                boolQuery.must(QueryBuilders.matchQuery("all", context));
            }
            request.source().query(boolQuery);
            //获取查询结果
            SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
            SearchHits responseHits = response.getHits();
            //获取查询结果总条数
            long total = responseHits.getTotalHits().value;
            //获取查询结果集
            SearchHit[] hits = responseHits.getHits();
            //将结果集中的json转为对象
            List<Media> mediaDtoList = new ArrayList<>();
            for (SearchHit hit : hits) {
                //获取json
                String sourceAsString = hit.getSourceAsString();
                Media media = JSON.parseObject(sourceAsString, Media.class);
                mediaDtoList.add(media);
            }
            return R.success(mediaDtoList);
        } catch (IOException e) {
            return R.error("请重试");
        }
    }

    @Override
    public R<String> deleteMediaByMediaId(String mediaId) throws IOException {
//        DeleteRequest request = new DeleteRequest(MEDIA_INDEX, mediaId.toString());
        DeleteRequest request = new DeleteRequest("test", mediaId);
        restHighLevelClient.delete(request, RequestOptions.DEFAULT);
        return R.success("文档删除成功");
    }

    @Override
    public R<String> saveMedia(Media media) {
        IndexRequest request = new IndexRequest();
        request.index(MEDIA_INDEX).id(media.getId()).source(JSON.toJSONString(media), XContentType.JSON);
        try {
            restHighLevelClient.index(request, RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
            return R.error("添加失败");
        }
        return R.success("添加成功");
    }

    /**
     * 通过时长码获取时长过滤上下限
     *
     * @param timeCode 时长码,根据时长码获取视频时长过滤条件的上下限。
     * @return 返回视频时长过滤条件上下限。
     */
    private String[] getTimeLimitByTimeCode(Integer timeCode) {
        String[] ans = new String[2];
        switch (timeCode) {
            case 1: {
                ans[0] = "00:00:00.00";
                ans[1] = "00:10:00.00";
                break;
            }
            case 2: {
                ans[0] = "00:10:00.00";
                ans[1] = "00:30:00.00";
                break;
            }
            case 3: {
                ans[0] = "00:30:00.00";
                ans[1] = "01:00:00.00";
                break;
            }
            case 4: {
                ans[0] = "01:00:00.00";
                ans[1] = "24:00:00.00";
                break;
            }
        }
        return ans;
    }

    private String getOrderFieldByOrderCode(Integer orderCode) {
        String field = "";
        switch (orderCode) {
            case 1: {
                field = "views";
                break;
            }
            case 2: {
                field = "uploadTime";
                break;
            }
        }
        return field;
    }

}
